﻿using Logger.Service.Interface;
using NCD.Common.Helpers;
using System.Net.WebSockets;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using WebSocketApi.CacheManager;
using WebSocketApi.Common;
using WebSocketApi.DataValidate.Interface;
using WebSocketApi.Extensions;
using WebSocketApi.Interface;
using WebSocketApi.Model;
using WebSocketApi.Request;
using WebSocketApi.SocketHandler;

namespace WebSocketApi.SocketHandler
{
    /// <summary>
    /// Socket连接对象处理类
    /// </summary>
    public class SocketServiceHandler : ISocketServiceHandler
    {
        private readonly ISocketDataManager _sockets;
        private readonly ILoggerContext _logger;
        private readonly INoticeCacheManager _noticeCacheManager;
        private readonly IUserValidateService _userValidateService;
        private readonly ICacheContext _cacheContext;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sockets"></param>
        /// <param name="logger"></param>
        /// <param name="noticeCacheManager"></param>
        /// <param name="userValidateService"></param>
        /// <param name="cacheContext"></param>
        public SocketServiceHandler(ISocketDataManager sockets, ILoggerContext logger, INoticeCacheManager noticeCacheManager,
            IUserValidateService userValidateService, ICacheContext cacheContext)
        {
            _sockets = sockets;
            _logger = logger;
            _noticeCacheManager = noticeCacheManager;
            _userValidateService = userValidateService;
            _cacheContext = cacheContext;
        }

        /// <summary>
        /// 连接Socket
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public override async Task OnConnectedAsync(WebSocket socket)
        {
            try
            {
                var socketId = Guid.NewGuid().ToString("N");
                await _sockets.AddSocket(socketId, socket);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("连接Socket异常：", e);
            }
        }

        /// <summary>
        /// 断开连接的Socket
        /// </summary>
        /// <param name="socket"></param>
        public override async Task OnDisconnectedAsync(WebSocket socket)
        {
            try
            {
                var socketId = _sockets.GetSocketId(socket);
                if (socketId != null)
                {
                    await SendCloConnectedNoticeAsync(socketId);
                    await _sockets.RemoveSocketAsync(socketId);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("连接关闭，清除内存中的Socket对象发生异常：", e);
            }
        }

        /// <summary>
        /// 单发消息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public override async Task SendMessageAsync(SingleMessageRequest request)
        {
            try
            {
                var connection = _sockets.GetAllConnections().Where(it => it.Key == request.SocketId)
                    .Select(it => new
                    {
                        SocketId = it.Key,
                        Socket = it.Value
                    }).FirstOrDefault();

                if (connection != null)
                {
                    if (request.MessageType != MessageTypeEnum.Files)
                    {
                        await SendMessageAsync(connection.Socket, request.Message.ToString());
                    }
                    else
                    {
                        await SendFileMessageAsync(connection.Socket, (Stream)request.Message);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("单发消息：", e);
            }
        }

        /// <summary>
        /// 指定连接发送
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public override async Task SendGroupMessageAsync(GroupMessageRequest request)
        {
            try
            {
                var list = _sockets.GetAllConnections().Select(it => new
                {
                    SocketId = it.Key,
                    Socket = it.Value
                }).ToList();

                if (request.SocketIds != null && request.SocketIds?.Length > 0)
                {
                    list = list.Where(it => request.SocketIds.Contains(it.SocketId)).ToList();
                }

                if (list?.Count == 0)
                {
                    throw new Exception("连接不存在");
                }

                if (request.MessageType != MessageTypeEnum.Files)
                {
                    //群发文本
                    foreach (var connection in list)
                    {
                        await SendMessageAsync(connection.Socket, request.Message.ToString());
                    }
                }
                else
                {
                    //群发附件
                    foreach (var connection in list)
                    {
                        await SendFileMessageAsync(connection.Socket, (Stream)request.Message);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("指定连接发送：", e);
            }
        }

        /// <summary>
        /// 发送全部
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public override async Task SendMassMessageAsync(MassMessageRequest request)
        {
            try
            {
                //过滤发送者、发送者不需要自己发送的消息
                var list = _sockets.GetAllConnections()
                    .Where(w => w.Key != request.FromSocketId)
                    .Select(it => new
                    {
                        SocketId = it.Key,
                        Socket = it.Value
                    }).ToList();

                if (list?.Count == 0)
                {
                    _logger.Error("发送全部，Socket连接不存在");
                    throw new Exception("连接不存在");
                }
                if (request.Message == null)
                {
                    _logger.Error("发送全部，发送消息为空");
                    throw new Exception("消息内容为空");
                }

                if (request.MessageType != MessageTypeEnum.Files)
                {
                    //群发文本
                    foreach (var connection in list)
                    {
                        await SendMessageAsync(connection.Socket, request.Message.ToString());
                    }
                }
                else
                {
                    //群发附件
                    foreach (var connection in list)
                    {
                        await SendFileMessageAsync(connection.Socket, (Stream)request.Message);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("SendMassMessageAsync：", e);
            }
        }

        /// <summary>
        /// 发送附件数据
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        private async Task SendFileMessageAsync(WebSocket socket, Stream stream)
        {
            if (socket.State != WebSocketState.Open)
            {
                throw new Exception("连接已关闭");
            };
            if (stream == null)
            {
                throw new Exception("附件数据不存在");
            }
            try
            {
                byte[] content = new byte[stream.Length];
                await stream.ReadAsync(content, 0, content.Length);
                stream.Seek(0, SeekOrigin.Begin);
                await socket.SendAsync(new ArraySegment<byte>(content), WebSocketMessageType.Binary, true, CancellationToken.None);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private async Task SendMessageAsync(WebSocket socket, string message)
        {
            try
            {
                if (socket.State != WebSocketState.Open)
                {
                    throw new Exception("连接已关闭");
                };
                byte[] content = Encoding.UTF8.GetBytes(message);
                await socket.SendAsync(new ArraySegment<byte>(content), WebSocketMessageType.Text, true, CancellationToken.None);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("发送文本消息：", e);
            }
        }

        /// <summary>
        /// 处理文本消息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="message"></param> 
        /// <returns></returns>
        public override async Task ReceiveTextAsync(WebSocket socket, string message)
        {

            var socketId = _sockets.GetSocketId(socket);
            var macAddress = IPHelper.GetMacAddress();
            if (!string.IsNullOrEmpty(socketId))
            {
                /*
                 * 1、按照Token模型反序列化消息体，
                 * 2、如果反序列化TokenModel异常则以消息体模型反序列化 
                 */
                var userToken = new UserTokenModel();
                try
                {
                    userToken = JsonSerializer.Deserialize<UserTokenModel>(message);
                }
                catch
                {
                    Console.WriteLine($"消息体格式：{message}");
                }
                //如果重新建立令牌连接，确保按照令牌验证的消息格式处理
                if (!string.IsNullOrEmpty(userToken?.UserToken) && !socketId.Contains($"_{macAddress}"))
                {
                    _logger.Debug($"{socketId}_建立Socket令牌");
                    //未验证的Socket令牌、建立Account与Socket关联
                    await HandleSocketTokenAsync(socket, message);

                    var userInfo = new SocketUserInfo
                    {
                        MacAddress = NCD.Common.Helpers.IPHelper.GetMacAddress(),
                        UserAccount = userToken.UserToken,
                        UserName = userToken.UserName
                    };
                    //await _socketUserManagerService.AddUser(userInfo);
                    Console.WriteLine($"当前在线连接数{_sockets.GetCount()}...");
                }
                else
                {
                    /* 当Socket连接验证通过后，则是双方数据正式通讯 
                     * 1、反序列化消息体、从消息体中查找DstAccount作为接受对象
                     * 2、依据DstAccount查找对应连接的SocketId
                     * 3、发送指定Socket
                     */
                    _logger.Debug($"发送消息:{message}");
                    await HandleNoticeToOnlineSocketAsync(socket, message);
                }
            }
        }

        /// <summary>
        /// 接收附件消息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="result"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override async Task ReceiveFileAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
        {
            try
            {
                var socketId = _sockets.GetSocketId(socket);
                var macAddress = IPHelper.GetMacAddress();
                if (!string.IsNullOrEmpty(socketId) && buffer.Length > 0)
                {
                    if (socketId.Contains($"_{macAddress}"))
                    {
                        //以文件发送、则需要分两次发送、先推送对应关系。
                        var stream = new MemoryStream(buffer);
                        await SendMassMessageAsync(new MassMessageRequest
                        {
                            Message = stream,
                            FromSocketId = socketId,
                            MessageType = MessageTypeEnum.Files
                        });
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("接收附件消息：", e);
            }
        }

        /// <summary>
        /// 建立连接
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private async Task<bool> HandleSocketTokenAsync(WebSocket socket, string message)
        {
            try
            {
                /*
                 * 1、当客户端发起连接请求、此处主动响应消息给客户端、告知当前连接是否生效。
                 * 2、具体客户端收发将经过该请求处理
                 * 3、当验证通过后、登录账号需要与socketId关联、方便查找
                 * 4、
                 */
                if (string.IsNullOrWhiteSpace(message))
                {
                    return false;
                }
                bool verifyAccountResponse = VerifySocketAccount(message);
                if (!verifyAccountResponse)
                {
                    return false;
                }

                //变更存储中的websocket、同时在Key中标记已验证
                var socketId = _sockets.GetSocketId(socket);
                if (string.IsNullOrWhiteSpace(socketId))
                {
                    return false;
                }

                var userToken = JsonSerializer.Deserialize<UserTokenModel>(message);
                var verifyResult = await _userValidateService.ValidateUserAsync(userToken.UserToken);
                if (verifyResult)
                {
                    //变更SocketId格式如：UserAccount01_macAddress、标记此连接已经过权限校验
                    var macAddress = IPHelper.GetMacAddress();
                    var newSocketId = $"{IdGenerate.GenerateMd5ByRequest(userToken?.UserToken)}_{macAddress}";
                    //清除可能存在的连接
                    if (socketId != newSocketId)
                    {
                        await _sockets.EditSocketKey(socketId, newSocketId);
                    }
                    _ = GetValidateSuccessResponseAsync(socket, socketId);
                    //检查是否有离线消息、如果有，则全部推送。
                    var cacheNoticeKey = $"{IdGenerate.GenerateMd5ByRequest(userToken?.UserToken)}_{macAddress}";
                    HandleSocketHistoryNoticesAsync(cacheNoticeKey, socket);

                }
                else
                {
                    //授权验证失败处理：
                    _ = GetValidateExceptionResponse(socket, socketId);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error($"解析Socket请求令牌异常：{e.Message}", e);
            }
            return false;
        }

        /// <summary>
        /// 验证用户模块
        /// </summary>
        /// <param name="tokenStr"></param>
        /// <returns></returns>
        private bool VerifySocketAccount(string tokenStr)
        {
            //macAddress 用于识别账号？如果有账号则不需要校验Mac地址
            var result = false;
            var userToken = JsonSerializer.Deserialize<UserTokenModel>(tokenStr);
            if (!string.IsNullOrWhiteSpace(userToken?.UserToken) && !string.IsNullOrWhiteSpace(userToken.UserName))
            {
                result = true;
            }
            return result;
        }

        /// <summary>
        /// 转发文本消息
        /// </summary>
        /// <returns></returns>
        private async Task HandleNoticeToOnlineSocketAsync(WebSocket socket, string message)
        {

            /* 消息体格式：
             * 确保可以从消息体中定位到发送方、接收方、操作指令，消息体中必须包含
             * Command,SrcAccount,DstAccount
             *{
                "Command": 0,
                "CommandName": "请求分析",
                "SrcAccount": "sqray",
                "SrcAccountName": "生强科技",
                "DstAccount": "tsinghuaai",
                "DstAccountName": "清华AI服务"
              } 
             */
            var macAddress = IPHelper.GetMacAddress();
            //兼容接收人嵌套格式
            string dstAccount = string.Empty;
            int? analyseCommand = null;
 
            if (string.IsNullOrWhiteSpace(dstAccount))
            {
                _logger.Error("接收方为空...");
                _logger.Error($"消息内容：{message}");
            }
            else
            {
                var recipientId = $"{IdGenerate.GenerateMd5ByRequest(dstAccount)}_{macAddress}";
                var recipientWebSocket = _sockets.GetSocketById(recipientId);
                if (recipientWebSocket == null)
                {
                    _logger.Error($"接收人{dstAccount}不在线或未建立令牌"); 
                    if (!string.IsNullOrWhiteSpace(dstAccount))
                    {
                        _logger.Debug("缓存消息到Redis");
                        var cacheKey = $"{IdGenerate.GenerateMd5ByRequest(dstAccount)}_{macAddress}";
                        _noticeCacheManager.AddNotice(cacheKey, message);
                        //推送连接不在线(发送给当前连接Socket)
                        await SendCloConnectedNoticeAsync(null);
                    }
                }
                else
                {
                    //接收对象在线并且已经建立令牌，立即推送
                    await SendMessageAsync(recipientWebSocket, message);
                    //推送成功、记录消息到发送记录列表,确保必须建立令牌后才可获取
                    //消息历史记录保存格式：
                    /*
                     * History: [
                     *  {messageInfo}
                     * ]
                     */
                    //历史消息堆积会比较大，使用日期分割
                    var newKey = $"{DateTime.Now.ToString("yyyy-MM-dd")}:{dstAccount}";
                    _noticeCacheManager.AddNoticeRecord(newKey, message);
                    _logger.Debug("记录消息到Redis");
                }
            }
        }

        /// <summary>
        /// 建立连接成功返回处理
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="socketId"></param>
        /// <returns></returns>
        private async Task GetValidateSuccessResponseAsync(WebSocket socket, string socketId)
        {
            var response = new UserTokenValidateResponse
            {
                Status = (int)TokenValidateStatusEnum.SUCCESS,
                Message = "连接成功"
            };
            await SendMessageAsync(socket, JsonSerializer.Serialize(response, new JsonSerializerOptions
            {
                Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
            }));
            var logMsg = $"建立连接成功，Socket连接:SocketId:{socketId}";
            _logger.Debug(logMsg);
            Console.WriteLine(logMsg);
        }

        /// <summary>
        /// 建立连接失败返回处理
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="socketId"></param>
        /// <returns></returns>
        private async Task GetValidateExceptionResponse(WebSocket socket, string socketId)
        {
            var response = new UserTokenValidateResponse
            {
                Status = (int)TokenValidateStatusEnum.FAILED,
                Message = "与服务器建立连接失败"
            };
            await SendMessageAsync(socket, JsonSerializer.Serialize(response, new JsonSerializerOptions
            {
                Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
            }));
            //建立连接失败后、删除缓存中的Socket连接对象、服务端主动断开
            await _sockets.RemoveSocketAsync(socketId);
            var logMsg = $"建立连接失败，已清除Socket连接:SocketId:{socketId}";
            _logger.Debug(logMsg);
            Console.WriteLine(logMsg);
        }

        /// <summary>
        /// 推送离线消息
        /// </summary>
        /// <param name="cacheNoticeKey"></param>
        /// <param name="socket"></param>
        /// <returns></returns>
        private void HandleSocketHistoryNoticesAsync(string cacheNoticeKey, WebSocket socket)
        {
            try
            {

                var historyNotices = _noticeCacheManager.GetUserAllHistoryNotices(cacheNoticeKey);
                if (historyNotices != null)
                {
                    foreach (var notice in historyNotices)
                    {
                        _ = SendMessageAsync(socket, notice.ToString());
                    }
                    var logMsg = $"建立连接成功,接收历史消息{historyNotices.Count}条";
                    _logger.Debug(logMsg);
                    Console.WriteLine(logMsg);
                    //推送成功后、清除离线消息
                    Console.WriteLine(cacheNoticeKey);
                    _noticeCacheManager.RemoveNotice(cacheNoticeKey);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("推送离线消息异常：", e);
            }
        }

        /// <summary>
        /// 通知接收用户不在线
        /// </summary>
        /// <returns></returns>
        private async Task SendCloConnectedNoticeAsync(string socketId)
        {
            //通知所有在线用户、xxx用户下线
            try
            {
                //读取缓存中的用户名xxx，组装下线通知格式：
                var userName = "Test-1";
                var noticeTemplate = $"用户{userName}已下线";
                //通知所有当前连接用户
                await SendMassMessageAsync(new MassMessageRequest
                {
                    FromSocketId = socketId,
                    Message = noticeTemplate,
                    MessageType = MessageTypeEnum.Text
                });
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Error("SendCloConnectedNoticeAsync", e);
            }
        }
    }
}
